package com.wei.czz.common.utils;

import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.exception.CzzException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-09-06 11:54:33
 * className: RsaUtils Rsa加解密工具类
 * version: 1.0
 * description:
 */
public class RsaUtils {

    private static final Logger log = LoggerFactory.getLogger(RsaUtils.class);

    /**
     * 算法名称
     */
    private final static String ALGORITHMS = "RSA";

    /**
     * 算法类型
     */
    private final static String SIGN_ALGORITHMS = "SHA1WithRSA";

    /**
     * 生成Rsa加解密算法密钥
     * @param keySize 密钥大小
     * @return 密钥数组[0-公钥 1-私钥]
     */
    public static String[] generateKey(int keySize) {
        if (keySize < 512) {
            log.error("Rsa加解密算法密钥长度值不符合条件。keySize={}", keySize);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
        KeyPairGenerator kpg;
        try {
            kpg = KeyPairGenerator.getInstance(ALGORITHMS);
        } catch (NoSuchAlgorithmException e) {
            log.error("获取Rsa算法密钥生成对象失败。message={}", e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
        // 初始化密钥长度
        kpg.initialize(keySize);
        // 生成密钥
        KeyPair keyPair = kpg.generateKeyPair();
        // 获取公钥
        String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
        // 获取公钥
        String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());

        return new String[] { publicKey, privateKey };
    }

    /**
     * 生成Rsa签名
     * <p>
     * 注意：
     * <p>
     * 1、使用私钥对明文进行Rsa签名
     * <p>
     * 2、使用 signRsa 进行签名生成，verifyRsa 进行签名校验
     * @param plainText  明文
     * @param privateKey 私钥
     * @return 签名结果
     */
    public static String signRsa(String plainText, String privateKey) {
        if (StringUtils.isAnyBlank(plainText, privateKey)) {
            log.warn("Rsa签名参数错误。plainText={} privateKey={}", plainText, privateKey);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
        // 获取私钥对象
        PrivateKey privateKeyObj = getPrivateKey(privateKey);
        try {
            // 获取签名对象
            Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
            // 初始化签名对象
            signature.initSign(privateKeyObj);
            // 设置明文内容
            signature.update(plainText.getBytes(StandardCharsets.UTF_8));
            // RSA签名
            byte[] signed = signature.sign();
            // 格式化返回
            return Base64.getEncoder().encodeToString(signed);
        } catch (NoSuchAlgorithmException e) {
            log.error("生成{}算法签名对象失败。message={}", SIGN_ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch (SignatureException e) {
            log.error("{}签名操作失败。plainText={} privateKey={} message={}", SIGN_ALGORITHMS, plainText, privateKey,
                    e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch(Exception e) {
            log.error("{}算法签名生成失败。plainText={} privateKey={} message={}", ALGORITHMS, plainText, privateKey,
                    e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
    }

    /**
     * Rsa签名校验
     * <p>
     * 注意：
     * <p>
     * 1、使用公钥对密文进行校验
     * <p>
     * 2、使用 signRsa 进行签名生成，verifyRsa 进行签名校验
     * @param plainText  原始明文
     * @param cipherText 密文
     * @param publicKey  公钥
     * @return 解密结果
     */
    public static boolean verifyRsa(String plainText, String cipherText, String publicKey) {
        if (StringUtils.isAnyBlank(plainText, cipherText, publicKey)) {
            log.warn("Rsa签名校验参数错误。plainText={} cipherText={} publicKey={}", plainText, cipherText, publicKey);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
        // 获取公钥对象
        PublicKey publicKeyObj = getPublicKey(publicKey);
        try {
            // 获取签名对象
            Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
            // 初始化签名对象
            signature.initVerify(publicKeyObj);
            // 设置原始明文内容
            signature.update(plainText.getBytes(StandardCharsets.UTF_8));
            // RSA密文校验
            return signature.verify(Base64.getDecoder().decode(cipherText.getBytes(StandardCharsets.UTF_8)));
        } catch (NoSuchAlgorithmException e) {
            log.error("生成{}签名对象失败。message={}", SIGN_ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch (SignatureException e) {
            log.error("{}签名操作失败。plainText={} publicKey={} message={}", SIGN_ALGORITHMS, plainText, publicKey,
                    e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch(Exception e) {
            log.error("RSA密文校验失败。plainText={} publicKey={} message={}", plainText, publicKey, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
    }

    /**
     * Rsa加密
     * <p>
     * 注意：
     * <p>
     * 1、使用公钥对明文进行加密
     * <p>
     * 2、encodeRsa 进行加密操作，decodeRsa 进行解密操作
     * @param plainText 明文
     * @param publicKey 公钥
     * @return 密文
     */
    public static String encodeRsa(String plainText, String publicKey) {
        if (StringUtils.isAnyBlank(plainText, publicKey)) {
            log.warn("Rsa加密参数错误。plainText={} publicKey={}", plainText, publicKey);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
        // 获取公钥对象
        PublicKey publicKeyObj = getPublicKey(publicKey);
        // 执行加密操作
        return doEncodeRsa(plainText, publicKeyObj);
    }

    /**
     * Rsa加密
     * <p>
     * 注意：
     * <p>
     * 1、使用私钥对明文进行加密
     * <p>
     * 2、encryptRsa 进行加密操作，decryptRsa 进行解密操作
     * @param plainText  明文
     * @param privateKey 私钥
     * @return 密文
     */
    public static String encryptRsa(String plainText, String privateKey) {
        if (StringUtils.isAnyBlank(plainText, privateKey)) {
            log.warn("Rsa加密参数错误。plainText={} privateKey={}", plainText, privateKey);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
        // 获取公钥对象
        PrivateKey privateKeyObj = getPrivateKey(privateKey);
        // 执行加密操作
        return doEncodeRsa(plainText, privateKeyObj);
    }

    /**
     * Rsa解密
     * <p>
     * 注意：
     * <p>
     * 1、使用私钥对密文进行解密
     * <p>
     * 2、encodeRsa 进行加密操作，decodeRsa 进行解密操作
     * @param cipherText 密文
     * @param privateKey 私钥
     * @return 明文
     */
    public static String decodeRsa(String cipherText, String privateKey) {
        if (StringUtils.isAnyBlank(cipherText, privateKey)) {
            log.warn("Rsa解密参数错误。cipherText={} privateKey={}", cipherText, privateKey);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
        // 获取私钥对象
        PrivateKey privateKeyObj = getPrivateKey(privateKey);
        // 执行解密操作
        return doDecodeRsa(cipherText, privateKeyObj);
    }

    /**
     * Rsa解密
     * <p>
     * 注意
     * <p>
     * 1、使用公钥对密文进行解密
     * <p>
     * 2、encryptRsa 进行加密操作，decryptRsa 进行解密操作
     * @param cipherText 密文
     * @param publicKey 公钥
     * @return 明文
     */
    public static String decryptRsa(String cipherText, String publicKey) {
        if (StringUtils.isAnyBlank(cipherText, publicKey)) {
            log.warn("Rsa解密参数错误。cipherText={} publicKey={}", cipherText, publicKey);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
        // 获取私钥对象
        PublicKey publicKeyObj = getPublicKey(publicKey);
        // 执行解密操作
        return doDecodeRsa(cipherText, publicKeyObj);
    }

    /**
     * 执行Rsa加密
     * @param plainText 明文
     * @param key       密钥键对象
     * @return 密文
     */
    private static String doEncodeRsa(String plainText, Key key) {
        try {
            // 获取Rsa算法的密钥器
            Cipher cipher = Cipher.getInstance(ALGORITHMS);
            // 初始化Rsa算法的密钥器
            cipher.init(Cipher.ENCRYPT_MODE, key);
            // Rsa加密
            byte[] result = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
            // 格式化返回
            return Base64.getEncoder().encodeToString(result);
        } catch (NoSuchAlgorithmException e) {
            log.error("{}加密，获取密码器对象失败，不是已知算法。message={}", ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch (NoSuchPaddingException e) {
            log.error("{}加密，获取密码器对象失败，不是已知补码方式。message={}", ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch (InvalidKeyException e) {
            log.error("{}加密，初始化密码器对象失败。message={}", ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch (IllegalBlockSizeException e) {
            log.error("{}算法加密失败。message={}", ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch (BadPaddingException e) {
            log.error("{}算法加密失败，补码操作失败。message={}", ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
    }

    /**
     * 执行Rsa解密
     * @param cipherText 密文
     * @param key        密钥键对象
     * @return 明文
     */
    private static String doDecodeRsa(String cipherText, Key key) {
        try {
            // 获取Rsa算法的密钥器
            Cipher cipher = Cipher.getInstance(ALGORITHMS);
            // 初始化Rsa算法的密钥器
            cipher.init(Cipher.DECRYPT_MODE, key);
            // Rsa解密
            byte[] result = cipher.doFinal(Base64.getDecoder().decode(cipherText.getBytes(StandardCharsets.UTF_8)));
            // 格式化返回
            return new String(result, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException e) {
            log.error("{}解密，获取密码器对象失败，不是已知算法。message={}", ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch (NoSuchPaddingException e) {
            log.error("{}解密，获取密码器对象失败，不是已知补码方式。message={}", ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch (InvalidKeyException e) {
            log.error("{}解密，初始化密码器对象失败。message={}", ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch (IllegalBlockSizeException e) {
            log.error("{}算法解密失败。message={}", ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        } catch (BadPaddingException e) {
            log.error("{}算法解密失败，补码操作失败。message={}", ALGORITHMS, e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
    }

    /**
     * 获取Rsa私钥对象
     * @param priKey 私钥
     * @return Rsa私钥对象
     */
    private static PrivateKey getPrivateKey(String priKey) {
        KeyFactory keyFactory;
        try {
            keyFactory = KeyFactory.getInstance(ALGORITHMS);
        } catch (NoSuchAlgorithmException e) {
            log.error("获取RSA加密键生成工厂对象失败。message={}", e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
        try {
            byte[] encodedKey = Base64.getDecoder().decode(priKey.getBytes(StandardCharsets.UTF_8));

            return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
        } catch (InvalidKeySpecException e) {
            log.error("生成RSA加密私钥对象失败。message={}", e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
    }

    /**
     * 获取Rsa公钥对象
     * @param publicKey 公钥
     * @return Rsa公钥对象
     */
    private static PublicKey getPublicKey(String publicKey) {
        KeyFactory keyFactory;
        try {
            keyFactory = KeyFactory.getInstance(ALGORITHMS);
        } catch (NoSuchAlgorithmException e) {
            log.error("获取RSA加密键生成工厂对象失败。message={}", e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
        try {
            byte[] bytes = Base64.getDecoder().decode(publicKey.getBytes(StandardCharsets.UTF_8));

            return keyFactory.generatePublic(new X509EncodedKeySpec(bytes));
        } catch (InvalidKeySpecException e) {
            log.error("生成RSA加密公钥钥对象失败。message={}", e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }
    }

    /*public static void main(String[] args) {

//        String[] keys = RsaUtils.generateKey(1024);
//        System.out.println("publicKey = " + keys[0]);
//        System.out.println("privateKey = " + keys[1]);

        String plainText = "123456789";
        String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCGeoqiKMJh0zgMTiZnYKv/k6KVpnbe9pi9dlea/WR7xf9qTYuUkXxW5ikiSefUZkLCSlM1NlfeDx6TEUvtmbWQBdV76Al2MwtGuH6P3/M1sQohR2abhbaFlv0RMtkg4QykFEWkkiLHHhWGuy8v/XC8jrQXKF5+l7CgokJJTrkTGQIDAQAB";
        String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIZ6iqIowmHTOAxOJmdgq/+TopWmdt72mL12V5r9ZHvF/2pNi5SRfFbmKSJJ59RmQsJKUzU2V94PHpMRS+2ZtZAF1XvoCXYzC0a4fo/f8zWxCiFHZpuFtoWW/REy2SDhDKQURaSSIsceFYa7Ly/9cLyOtBcoXn6XsKCiQklOuRMZAgMBAAECgYAdT+aZ9AJyqkSEQiCS1fqHqdl9fxxX+y5N1dbAnMzjQcQrmp17E78jfeWnaK6UmHmiueqrJZ4/c9I5QkLnlmKBoiH5bDSKV5JxCCsVAHrZQ1WY0e32FdCt+dB/S8mJTdM8MQJF9dWc+lkdxR/BsS/sItGaJZO8R6AhzLVZkj9bpQJBAMC6KenJSTkug6MTppX1L6uXv8ZrNaGTqtM/P/w/4l5Hj12ar3YK3CspUDbotf7JySFT0SAxshKi6jAP8DrKP+cCQQCyoNtahqqr0hswlWqmYI6lm71SMcmkky+8lSKD6ZBiidWJu1Z0QQOE55ZV3mCaEa6Ubi5IlbXce3PfcJUMY7T/AkEAvNnvVjo6eUDeFv+bflg69R8G+cx2SjUj8EqLTYfgLthaYLPB+Z6creKWyqdwJxHd971lL/Hid6aSarRDh6hFwwJAZc/9dsmO+cQVr0lasl4SsB3meLTcA6vI56nxNFKp8yAw0AIxMq3jvZrqOyM22Fn/saZjRwfd0j+jOdXHfnpOLQJAO5ZzBEQrLW1ZBMuq9JA7fhIPQvqLRY8HNVT4bTUuorH764n4Db/8Kndi4j3VRAp5dSUfoFX8yPHjrWGBIPNl1w==";


//        String cipherText = RsaUtils.signRsa(plainText, privateKey);
//        System.out.println("cipherText = " + cipherText);
////        plainText += "123";
//        boolean bool = RsaUtils.verifyRsa(plainText, cipherText, publicKey);
//        System.out.println("bool = " + bool);

//        String cipherText = RsaUtils.encodeRsa(plainText, publicKey);
//        System.out.println("cipherText = " + cipherText);
//        plainText = RsaUtils.decodeRsa(cipherText, privateKey);
//        System.out.println("plainText = " + plainText);

//        String cipherText = RsaUtils.encryptRsa(plainText, privateKey);
//        System.out.println("cipherText = " + cipherText);
//        plainText = RsaUtils.decryptRsa(cipherText, publicKey);
//        System.out.println("plainText = " + plainText);


    }*/
}
